﻿using Leo.Awesome.Attachment.Config;
using Leo.Awesome.Attachment.Domain;
using Leo.Awesome.Attachment.Enums;
using Leo.Awesome.Attachment.Repositories;
using Leo.Awesome.DictionaryManagement.Dtos;
using Leo.Core.Application;
using Leo.Core.Util;
using Leo.Core.Util.Extension;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.BlobStoring;
using Volo.Abp.Domain.Repositories;

namespace Leo.Awesome.Attachment.Services
{
    public class AttachmentService : BaseCrudAppService<
        Attachment,
        AttachmentGetOutPutDto,
        AttachmentGetListOutputDto,
        AttachmentPagedResultDto,
        Guid,
        AttachmentGetListInput,
        AttachmentCreateUpdateInput,
        AttachmentCreateUpdateInput,
        AttachmentGetListPageInput>, IAttachmentService
    {
        // 注入类型化Blob容器接口
        private readonly IBlobContainer<FilesContainer> _blobContainer;

        private readonly MinioOption _minioOption;
        private readonly IAttachmentRepostiry _attachmentRepostiry;
        private readonly ILogger<AttachmentService> _logger;
        public AttachmentService(
            IAttachmentRepostiry attachmentRepostiry,
            IBlobContainer<FilesContainer> blobContainer,
            ILogger<AttachmentService> logger,
            IOptions<MinioOption> minioOption)
            : base(attachmentRepostiry)
        {
            _attachmentRepostiry = attachmentRepostiry;
            _blobContainer = blobContainer;
            _minioOption = minioOption.Value;
            _logger = logger;
        }
        private readonly static Dictionary<FolderType, string> folderTypeMappings = new Dictionary<FolderType, string>
        {
            { FolderType.TempFolder, FolderType.TempFolder.ToString()},
            { FolderType.PersonalFolder, FolderType.PersonalFolder.ToString() },
            { FolderType.ProjectFolder, FolderType.ProjectFolder.ToString() }
        };    

        public override async Task<AttachmentGetOutPutDto> CreateAsync(AttachmentCreateUpdateInput input)
        {
            folderTypeMappings.TryGetValue(input.FolderType, out var folderPath);
            var currentUserId = input.UploaderId.HasValue ? $"{input.UploaderId.Value}/" : "";
            input.StoragePath = $"{_minioOption.FilePath}/{folderPath}/{currentUserId}";           
            input.MD5 = Md5Encrypt.Encrypt(input.FileContext);
            _logger.LogDebug($"文件流md5值:{input.MD5}");
            input.Id = GuidGenerator.Create();
            await _blobContainer.SaveAsync(input.Id.ToString(), input.FileContext).ConfigureAwait(false);         
            return await base.CreateAsync(input);
        }

        public override async Task<AttachmentGetOutPutDto> GetAsync(Guid id)
        {
            var entity = await _attachmentRepostiry.FirstOrDefaultAsync(x => x.Id == id);
            if (entity == null)
            {
                throw new BusinessException("文件不存在!");
            }
            var output = ObjectMapper.Map<Attachment, AttachmentGetOutPutDto>(entity);
            var stream = await _blobContainer.GetAsync(id.ToString());
            output.FileContext= stream.ReadAllBytes();
            return output;
        }

        public async Task<List<AttachmentGetOutPutDto>> InsertBatch(List<AttachmentCreateUpdateInput> inputs)
        {
            var list = new List<AttachmentGetOutPutDto>();
            var firstInput = inputs.FirstOrDefault();
            folderTypeMappings.TryGetValue(firstInput.FolderType, out var folderPath);
            var currentUserId = firstInput.UploaderId.HasValue ? $"{firstInput.UploaderId.Value}/" : "";
            var storagePath = $"{_minioOption.FilePath}/{folderPath}/{currentUserId}";
            foreach (var input in inputs)
            {
                input.MD5 = Md5Encrypt.Encrypt(input.FileContext);
                input.Id = GuidGenerator.Create();
                input.StoragePath = storagePath;
                await _blobContainer.SaveAsync(input.Id.ToString(), input.FileContext).ConfigureAwait(false);
                var output = await base.CreateAsync(input);
                list.Add(output);
            }
            return list;
        }

        public override async Task<List<AttachmentGetListOutputDto>> Query(AttachmentGetListInput getListInput)
        {
            var ids = getListInput.Ids;
            if (ids == null || !ids.Any())
            {
                throw new BusinessException("需要查询的Id集合不能为空");
            }
            if (ids.Count >= 50)
            {
                throw new BusinessException("查询数量不支持超过50");
            }
            var attachments = await _attachmentRepostiry.GetListAsync(l => ids.Contains(l.Id));
            var outputs = ObjectMapper.Map<List<Attachment>, List<AttachmentGetListOutputDto>>(attachments);
            foreach (var item in outputs)
            {
                var stream = await _blobContainer.GetAsync(item.Id.ToString());
                if (stream != null && stream.Length > 0)
                {
                    item.FileContext = stream.ReadAllBytes();
                }
            }
            return outputs;
        }

        public override async Task<PagedResultDto<AttachmentGetListOutputDto>> GetListAsync(AttachmentGetListInput input)
        {
            var outputs= await base.GetListAsync(input);
            foreach (var item in outputs.Items)
            {
                var stream = await _blobContainer.GetAsync(item.Id.ToString());
                if (stream != null && stream.Length > 0)
                {
                    item.FileContext = stream.ReadAllBytes();
                }
            }
            return outputs;
        }

        public override async Task<AttachmentGetOutPutDto> UpdateAsync(Guid id, AttachmentCreateUpdateInput input)
        {
            folderTypeMappings.TryGetValue(input.FolderType, out var folderPath);
            var currentUserId = input.UploaderId.HasValue ? $"{input.UploaderId.Value}/" : "";
            input.StoragePath = $"{_minioOption.FilePath}/{folderPath}/{currentUserId}";
            input.MD5 = Md5Encrypt.Encrypt(input.FileContext);
            await _blobContainer.DeleteAsync(id.ToString());
            await _blobContainer.SaveAsync(id.ToString(), input.FileContext);
            return await base.UpdateAsync(id, input);
        }

        public override async Task DeleteAsync(Guid id)
        {
            await _blobContainer.DeleteAsync(id.ToString());
            await base.DeleteAsync(id);
        }

        public async Task DeleteManyAsync(List<Guid> ids)
        {
            foreach (var id in ids)
            {
                await DeleteAsync(id);
            }
        }
    }
}
